home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Libraries / SAT 2.3a1 / Collision ⁄⁄⁄ ƒ / Collision ⁄⁄⁄.p < prev    next >
Encoding:
Text File  |  1994-11-03  |  34.6 KB  |  1,250 lines  |  [TEXT/PJMM]

  1. {Collision ///}
  2. {}
  3. {This demo demonstrates some alternative ways to use SAT:}
  4. {}
  5. {• The animation is called from the standard event loop (via TransSkel). This slows things down}
  6. {quite a bit (since all other processes are allowed to run), but makes the application background-}
  7. {friendly.}
  8. {• It runs in an ordinary, moveable window. We can, with some effort, do this while still}
  9. {using the fast mode (in which case we would have to restrict window dragging so it stays}
  10. {within the main screen, modify certain global (gSAT.ox ad gSAT.oy), and also limit the}
  11. {horizontal alignment of the window like HyperCard does), but in this demo we just use the}
  12. {slow (safe) mode. }
  13. {• We create sprites from QuickDraw calls instead of cicns.}
  14. {• We use the mask regions of the sprites for better collision detection.}
  15. {• We use a pattern as backkground instead of PICTs.}
  16. {• All the code is in one unit. This might make it less structured, less encapsulated, but I wanted}
  17. {to show you that you don't have to do things exactly the way I do in the other demos.}
  18. {• Using a modified sprite record.}
  19. {• Fixed-point positions}
  20. {}
  21. {However, some variations remain that aren't demonstrated even here:}
  22. {• Calculating the positions of the sprites with the system clock (TickCount or Time Manager) instead of}
  23. {moving them each frame. That approach has some advantages (i.e. constant speed on objects), can easily}
  24. {be used with SAT, but is not as simple.}
  25.  
  26.  
  27. program CollisionIII;
  28.  
  29.     uses
  30. {$ifc UNDEFINED THINK_PASCAL}
  31.         Types, QuickDraw, Events, Windows, Dialogs,
  32.         Fonts, DiskInit,
  33.         TextEdit, Traps, Desk, Memory, SegLoad, Scrap,
  34.         ToolUtils, OSEvents, OSUtils, Menus, Resources,
  35.         StandardFile, GestaltEqu, Files, Errors,
  36. {$endc}
  37.         TransSkel, customized_SAT;
  38.  
  39.     const
  40.         newgameItem = 1;
  41.         clearHighItem = 3;
  42.  
  43.         aboutAlrt = 128;
  44.         fileMenuRes = 128;
  45.         shapeMenuRes = 129;
  46.         theWindRes = 128;
  47.  
  48.         kGameTime = 3600; {60 sekunder}
  49.         kExtraTime = 180; {3 sekunder}
  50.         kLevelBonus = 25;
  51.  
  52.     type
  53.         SettingsRec = record
  54.                 high: Longint;
  55.                 player: string[5];
  56.             end;
  57.         SettingsPtr = ^SettingsRec;
  58.         SettingsHnd = ^SettingsPtr;
  59.     var
  60.         settings: SettingsHnd;
  61.         fileMenu, shapeMenu: MenuHandle;
  62.         gameRunning: Boolean;
  63.         gameStartTime, lastSetStartTime: Longint;
  64.         setCount: integer;
  65.         gMode: integer;
  66.         scoreFace, highFace, lastface: FacePtr;
  67.         myFace, welcomeFace: FacePtr;
  68.         score: Longint;
  69.         bgPat: SATPatHandle;
  70.  
  71.         scaledFace: array[0..31] of FacePtr;
  72.  
  73.     procedure Barf;
  74.     begin
  75.         ReportStr('Something went wrong. Sorry.');
  76.         halt;
  77.     end;
  78.  
  79. {Ljud:}
  80.  
  81. {Konstruera en snd-resurs artificiellt}
  82. {Rutinen bygger en handle med reserverad plats för ljudet, som sedan värdrutinen kan skapa.}
  83.     function CreateSnd (size: longint; var sndH: handle; var dataPek: Ptr): Boolean;
  84.         type
  85.             mySndRec = packed record
  86.                     format: integer;
  87.                     numsynth: integer; {must be 0}
  88. {synth}
  89.                     synthid: integer;{5}
  90.                     synthinit: longint;{0}
  91.  
  92.                     numcom: integer; {must be 1}
  93. {command}
  94.                     command: integer;{ $8051}
  95.                     param1: integer; {0}
  96.                     param2: longint; { $14}
  97. {sound header}
  98.                     dataptr: Ptr;
  99.                     datasize: longint;
  100.                     samplerate: longint; {22kHz = $56ee8ba3}
  101.                     loopstart: Ptr;
  102.                     loopend: Ptr;
  103.                     encoding: Byte;{0}
  104.                     basenote: Byte; { $3c}
  105. {data}
  106.                     ljud: packed array[0..0] of Byte;
  107.                 end;
  108.             msrp = ^mySndRec;
  109.             msrh = ^msrp;
  110.         var
  111.             h: msrh;
  112.     begin
  113.         h := msrh(NewHandle(sizeof(mySndRec) + size));
  114.         if h = nil then
  115.             CreateSnd := false
  116.         else
  117.             begin
  118.                 HLock(Handle(h)); {Fixar detta buggen med att ljuden ändras?}
  119.                 with h^^ do
  120.                     begin
  121.                         format := 1;
  122.                         numsynth := 1;
  123.                         synthid := 5;
  124.                         synthinit := 0;
  125.                         numcom := 1;
  126.                         command := $8051;
  127.                         param1 := 0;
  128.                         param2 := $14;
  129.                         dataptr := @ljud[0];
  130.                         datasize := size;
  131.                         samplerate := $56ee8ba3; {div 2 - fast varför köra 11kHz när man synthar?!}
  132.                         loopstart := dataptr;
  133.                         loopend := dataptr; {?}
  134.                         encoding := 0;
  135.                         basenote := $3c;
  136.                         dataPek := dataptr; {Utdata}
  137.                     end; {with}
  138.                 SndH := handle(h);{utdata}
  139.                 CreateSnd := true;
  140.             end; {if nil else}
  141.     end;{CreateSnd}
  142.  
  143.     var
  144.         pushH, bippH, baeH: Handle;
  145.  
  146. {Fixa några bra subrutiner för ljudsyntning?!}
  147. {- Eko}
  148. {- Lågpass och högpass}
  149. {- Sampla upp eller ner?}
  150. {- Frekvensvariation?}
  151. {- Fade in, fade out (mm envelope)}
  152. {Drömmen är förstås FFT, så man kan göra riktigt vass bandspärr, frekvensskift mm.}
  153. {Apropå: kan man inte göra bra ljudkompression med FFT?}
  154.  
  155. {$PUSH}
  156. {$R-}
  157.  
  158. {Rutinen som skall bygga de syntetiska ljud vi önskar!}
  159.     procedure Synth;
  160.         type
  161.             ArtRec = record
  162.                     arr: packed array[0..10000] of Byte;
  163.                 end;
  164.             ArtPtr = ^ArtRec;
  165.         var
  166.             tmpPtr: ArtPtr;
  167.             i: integer;
  168.         const
  169.             pushSize = 3479;
  170.             bippSize = 2959;
  171.             baeSize = 20000;
  172.     begin
  173.         if not CreateSnd(pushSize + 1, pushH, Ptr(tmpptr)) then
  174.             CheckNoMem(nil); {EmergencyExit}
  175.         for i := 0 to pushSize do
  176.             tmpptr^.arr[i] := band(char(random), 127) * (pushSize - i) div pushSize + 128;
  177.         for i := 0 to pushSize - 3 do
  178.             tmpptr^.arr[i] := (tmpptr^.arr[i] + tmpptr^.arr[i + 1] + tmpptr^.arr[i + 2] + tmpptr^.arr[i + 3]) div 4;
  179.         for i := 0 to 64 do
  180.             begin
  181. {tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;}
  182.                 tmpptr^.arr[pushSize - i] := tmpptr^.arr[pushSize - i] * i div 64;
  183.             end;
  184.  
  185.         if not CreateSnd(bippSize + 1, bippH, Ptr(tmpptr)) then
  186.             CheckNoMem(nil); {EmergencyExit}
  187.         for i := 0 to bippSize do
  188.             tmpptr^.arr[i] := i mod (i div 171 + 1) mod 127 + 128; {mjiioo}
  189. {tmpptr^.arr[i] := i mod (i div 171 + 1) + 128; {mjiioo}
  190. {tmpptr^.arr[i] := i mod (i div 17 + 1) + 128; {maipp}
  191. {tmpptr^.arr[i] := band(i, 63) + 128;}
  192.         for i := 0 to 64 do
  193.             begin
  194.                 tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;
  195.                 tmpptr^.arr[bippSize - i] := tmpptr^.arr[bippSize - i] * i div 64;
  196.             end;
  197.  
  198.         if not CreateSnd(baeSize + 1, baeH, Ptr(tmpptr)) then
  199.             CheckNoMem(nil); {EmergencyExit}
  200.         for i := 0 to baeSize do
  201.             tmpptr^.arr[i] := (i div 5) mod (i div 1571 + 1) mod 127 + 128; {mjiioo}
  202. {tmpptr^.arr[i] := i mod (i div 171 + 1) + 128; {mjiioo}
  203. {tmpptr^.arr[i] := i mod (i div 17 + 1) + 128; {maipp}
  204. {tmpptr^.arr[i] := band(i, 63) + 128;}
  205.         for i := 0 to baeSize - 3 do
  206.             tmpptr^.arr[i] := (tmpptr^.arr[i] + tmpptr^.arr[i + 1] + tmpptr^.arr[i + 2] + tmpptr^.arr[i + 3]) div 4;
  207.         for i := 0 to 64 do
  208.             begin
  209.                 tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;
  210.                 tmpptr^.arr[baeSize - i] := tmpptr^.arr[baeSize - i] * i div 64;
  211.             end;
  212.     end;
  213. {$POP}
  214.  
  215.     procedure DoAbout;
  216.     begin
  217.         if 1 = Alert(aboutAlrt, nil) then
  218.             ;
  219.     end;
  220.  
  221. {Two handly routines from my dialog utilities unit.}
  222.     procedure SetTextDItem (theDialog: DialogPtr; itemNo: integer; theString: Str255);
  223.         var
  224.             kind: integer;
  225.             item: ControlHandle;
  226.             box: Rect;
  227.     begin
  228.         GetDItem(theDialog, itemNo, kind, Handle(item), box);
  229. {Check kind}
  230.         kind := BitAnd(kind, 127);
  231.         case kind of
  232.             8, 16: {statText, editText}
  233.                 SetIText(handle(item), theString);
  234.             0, 1, 2, 4, 5, 6: {button, checkbox, radio - men vad är 4?}
  235.                 SetCTitle(item, theString);
  236.             otherwise {Övriga har ingen text man kan sätta}
  237.                 SysBeep(1);
  238.         end;{case}
  239.     end;
  240.     function GetTextDItem (theDialog: DialogPtr; itemNo: integer): Str255;
  241.         var
  242.             kind: integer;
  243.             item: ControlHandle;
  244.             box: Rect;
  245.             tmpStr: Str255;
  246.     begin
  247.         GetDItem(theDialog, itemNo, kind, Handle(item), box);
  248. {Check kind}
  249.         kind := BitAnd(kind, 127);
  250.         tmpStr := '';
  251.         case kind of
  252.             8, 16: {statText, editText}
  253.                 GetIText(handle(item), tmpStr);
  254.             0, 1, 2, 4, 5, 6: {button, checkbox, radio…?}
  255.                 GetCTitle(item, tmpStr);
  256.             otherwise {Övriga har ingen text man kan sätta}
  257.                 SysBeep(1);
  258.         end;{case}
  259.         GetTextDItem := tmpStr;
  260.     end;
  261.     function MyNumToString (l: longint): Str255;
  262.         var
  263.             tmpStr: Str255;
  264.     begin
  265.         NumToString(l, tmpStr);
  266.         MyNumToString := tmpStr;
  267.     end;
  268.  
  269. {Make the new high score dialog}
  270.     procedure AskHigh;
  271.         const
  272.             highDlogID = 129;
  273.         var
  274.             dialog: DialogPtr;
  275.             oldPort: GrafPtr;
  276.             itemHit: integer;
  277.             str: str255;
  278.     begin
  279.         GetPort(oldPort);
  280.         dialog := GetNewDialog(highDlogID, nil, WindowPtr(-1));
  281.         ShowWindow(dialog);
  282.         SelectWindow(dialog);
  283.         SetPort(dialog);
  284.  
  285.         SetTextDItem(dialog, 3, settings^^.player);
  286.         SelIText(dialog, 3, 0, 32767);
  287.         itemHit := -1;
  288.         while (itemHit <> 1) and (itemHit <> 2) do { 1=ok, 2=cancel }
  289.             ModalDialog(nil, itemHit);
  290.         if itemHit = 1 then
  291.             begin
  292.                 str := GetTextDItem(dialog, 3);
  293.                 if length(str) > 5 then
  294.                     str[0] := char(5); {snabbaste sättet att korta den!}
  295.                 settings^^.player := str;
  296.                 settings^^.high := score;
  297.             end;
  298.         DisposeDialog(dialog);
  299.         SetPort(oldPort);
  300.     end;
  301.  
  302. {Reuseable sprite movement routine, called from all sprite handling routines. Some sprites use this}
  303. {as handling routine.}
  304.     procedure SATBounce (me: SpritePtr);
  305.     begin
  306.         me^.position.h := me^.position.h + me^.speed.h;
  307.         me^.position.v := me^.position.v + me^.speed.v;
  308.         if me^.position.h < 0 then
  309.             me^.speed.h := abs(me^.speed.h);
  310.         if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  311.             me^.speed.h := -abs(me^.speed.h);
  312.         if me^.position.v < 0 then
  313.             me^.speed.v := abs(me^.speed.v);
  314.         if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  315.             me^.speed.v := -abs(me^.speed.v);
  316.     end;
  317.  
  318. {The same but using fixed-point position, as in HandlePlayer}
  319.     procedure SATFixedBounce (me: SpritePtr);
  320.     begin
  321.         me^.fixedPos.h := me^.fixedPos.h + me^.speed.h;
  322.         me^.fixedPos.v := me^.fixedPos.v + me^.speed.v;
  323.  
  324.         me^.position.h := BSR(me^.fixedPos.h, 4); {Shift left 4 steps, i.e. div 16}
  325.         me^.position.v := BSR(me^.fixedPos.v, 4);
  326.  
  327. {Since BSR isn't aritmetic shift, a negative fixedPos will unfortunately result in}
  328. {a very large positive position. This must be accounted for when checking borders}
  329. {- or we could use div, but that is slower.}
  330.  
  331.         if me^.fixedPos.h < 0 then
  332.             begin
  333.                 me^.speed.h := abs(me^.speed.h);
  334.                 me^.position.h := 0;
  335.             end
  336.         else if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  337.             me^.speed.h := -abs(me^.speed.h);
  338.         if me^.fixedPos.v < 0 then
  339.             begin
  340.                 me^.speed.v := abs(me^.speed.v);
  341.                 me^.position.v := 0;
  342.             end
  343.         else if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  344.             me^.speed.v := -abs(me^.speed.v);
  345.     end;
  346.  
  347.  
  348.     procedure HandleTheSprite (me: SpritePtr);
  349.     begin
  350.         if me^.speed.h = 0 then
  351.             me^.speed.h := Rand(32) - Rand(32);
  352.         if me^.speed.v = 0 then
  353.             me^.speed.v := Rand(32) - Rand(32);
  354.         if me^.face = nil then
  355.             begin
  356.                 me^.face := myFace;
  357.                 if me^.face <> nil then
  358.                     me^.hotRect := me^.face^.iconMask.bounds;
  359.             end;
  360.         SATFixedBounce(me);
  361.     end;
  362.  
  363.     procedure RedrawScoreFace;
  364.     begin
  365.         SATSetPortFace(scoreFace);
  366.         EraseRect(scoreFace^.iconMask.bounds);
  367.         MoveTo(2, 14);
  368.         ForeColor(blackColor);
  369.         DrawString('Score:');
  370.         DrawLong(score);
  371.         ForeColor(whiteColor);
  372.         MoveTo(0, 12);
  373.         DrawString('Score:');
  374.         DrawLong(score);
  375.         ForeColor(blackColor);
  376.         SATSetPortScreen;
  377.         SATSetPortMask(scoreFace);
  378.         EraseRect(scoreFace^.iconMask.bounds);
  379.         MoveTo(0, 12);
  380.         DrawString('Score:');
  381.         DrawLong(score);
  382.         MoveTo(2, 14);
  383.         DrawString('Score:');
  384.         DrawLong(score);
  385.         SATSetPortScreen;
  386.         SATChangedFace(scoreFace);
  387.     end;
  388.  
  389.     procedure RedrawHighFace;
  390.         var
  391.             str: Str255;
  392.     begin
  393.         str := stringof('High score:', MyNumToString(settings^^.high), ' by ', settings^^.player);
  394.  
  395.         SATSetPortFace(highFace);
  396.         EraseRect(highFace^.iconMask.bounds);
  397.         MoveTo(2, 14);
  398.         ForeColor(blackColor);
  399.         DrawString(str);
  400.         ForeColor(whiteColor);
  401.         MoveTo(0, 12);
  402.         DrawString(str);
  403.         ForeColor(blackColor);
  404.         SATSetPortScreen;
  405.         SATSetPortMask(highFace);
  406.         EraseRect(highFace^.iconMask.bounds);
  407.         MoveTo(0, 12);
  408.         DrawString(str);
  409.         MoveTo(2, 14);
  410.         DrawString(str);
  411.         SATSetPortScreen;
  412.         SATChangedFace(highFace);
  413.     end;
  414.  
  415.     procedure RedrawLastFace;
  416.     begin
  417.         SATSetPortFace(lastface);
  418.         EraseRect(lastface^.iconMask.bounds);
  419.         MoveTo(2, 14);
  420.         DrawString('Last score:');
  421.         DrawLong(score);
  422.         ForeColor(whiteColor);
  423.         MoveTo(0, 12);
  424.         DrawString('Last score:');
  425.         DrawLong(score);
  426.         ForeColor(blackColor);
  427.         SATSetPortScreen;
  428.         SATSetPortMask(lastface);
  429.         EraseRect(lastface^.iconMask.bounds);
  430.         MoveTo(0, 12);
  431.         DrawString('Last score:');
  432.         DrawLong(score);
  433.         MoveTo(2, 14);
  434.         DrawString('Last score:');
  435.         DrawLong(score);
  436.         SATSetPortScreen;
  437.         SATChangedFace(lastface);
  438.     end;
  439.  
  440.     var
  441.         playerFace: array[0..15] of FacePtr;
  442.         playerSpeed: array[0..15] of Point;
  443.  
  444.  
  445.  
  446.  
  447.  
  448. {Redraw all player faces. This is separated from InitPlayerFaces since it must be called on}
  449. {depth changes.}
  450.     procedure ReDrawPlayerFaces;
  451.         const
  452.             totalAngle = 240;
  453.         var
  454.             i: integer;
  455.             r, r1, r2, ri: Rect;
  456.             reg1, reg2: RgnHandle;
  457.             pol: PolyHandle;
  458.     begin
  459.         SetRect(r, 0, 0, 40, 40); {Total face size}
  460.         SetRect(r1, 0, 0, 38, 38); {Colored part}
  461.         SetRect(ri, 9, 9, 29, 29); {Colored part, inner circle}
  462.         SetRect(r2, 2, 2, 40, 40); {Shadow}
  463.         for i := 0 to 15 do
  464.             begin
  465.                 reg1 := NewRgn;
  466.                 reg2 := NewRgn;
  467.  
  468. {Generate shape}
  469.                 SATSetPortMask(playerFace[i]);
  470.                 PaintArc(r1, i * 360 div 16 - (360 - totalAngle) div 2, totalAngle);
  471.                 EraseArc(ri, i * 360 div 16 - (360 - totalAngle) div 2, totalAngle); {360-graders-skala}
  472.                 if noErr <> BitMapToRegionGlue(reg1, playerFace[i]^.iconMask) then{}
  473.                     ;
  474.                 CopyRgn(reg1, reg2);
  475.                 OffsetRgn(reg2, 2, 2);
  476.  
  477. {Draw face}
  478.                 SATSetPortFace(playerFace[i]);
  479.                 EraseRect(playerFace[i]^.iconMask.bounds);
  480.                 ForeColor(blackColor);
  481.                 PaintRgn(reg2); {black "Shadow"}
  482.                 ForeColor(cyanColor);
  483.                 if gSAT.initDepth > 1 then
  484.                     PaintRgn(reg1) {If we run in color, fill it completely with cyan}
  485.                 else
  486. {$IFC UNDEFINED THINK_PASCAL}
  487.                     FillRgn(reg1, qd.ltGray); {If we run in b/w, a gray pattern looks nicer}
  488. {$ELSEC}
  489.                     FillRgn(reg1, ltGray); {If we run in b/w, a gray pattern looks nicer}
  490. {$ENDC}
  491.                 ForeColor(blueColor);
  492.                 FrameRgn(reg1);
  493.                 ForeColor(blackColor);
  494. {Draw mask}
  495.                 SATSetPortMask(playerFace[i]);
  496.                 EraseRect(playerFace[i]^.iconMask.bounds);
  497.                 PaintRgn(reg1);
  498.                 PaintRgn(reg2);
  499.                 SATSetPortScreen;
  500.                 SATChangedFace(playerFace[i]);
  501.  
  502.                 DisposeRgn(reg1);
  503.                 DisposeRgn(reg2);
  504.             end;
  505.     end;
  506.  
  507. {Create all player faces.}
  508.     procedure InitPlayerFaces;
  509.         var
  510.             i: integer;
  511.             r: Rect;
  512.     begin
  513. {We use crude approximations to the sine/cosine functions we really want.}
  514. {A real game might init the table by using sine and cosine for real, but I don't}
  515. {want to make this harder to read than it already is. A real game would also}
  516. {use more than 16 directions, say 32 or even 64.}
  517.  
  518.         SetPt(playerSpeed[6], 0, -6);
  519.         SetPt(playerSpeed[7], 2, -5);
  520.         SetPt(playerSpeed[8], 4, -4);
  521.         SetPt(playerSpeed[9], 5, -2);
  522.         SetPt(playerSpeed[10], 6, 0);
  523.         SetPt(playerSpeed[11], 5, 2);
  524.         SetPt(playerSpeed[12], 4, 4);
  525.         SetPt(playerSpeed[13], 2, 5);
  526.         SetPt(playerSpeed[14], 0, 6);
  527.         SetPt(playerSpeed[15], -2, 5);
  528.         SetPt(playerSpeed[0], -4, 4);
  529.         SetPt(playerSpeed[1], -5, 2);
  530.         SetPt(playerSpeed[2], -6, 0);
  531.         SetPt(playerSpeed[3], -5, -2);
  532.         SetPt(playerSpeed[4], -4, -4);
  533.         SetPt(playerSpeed[5], -2, -5);
  534.  
  535.         SetPt(playerSpeed[6], 0, -32);
  536.         SetPt(playerSpeed[7], 14, -28);
  537.         SetPt(playerSpeed[8], 22, -22);
  538.         SetPt(playerSpeed[9], 28, -14);
  539.         SetPt(playerSpeed[10], 32, 0);
  540.         SetPt(playerSpeed[11], 28, 14);
  541.         SetPt(playerSpeed[12], 22, 22);
  542.         SetPt(playerSpeed[13], 14, 28);
  543.         SetPt(playerSpeed[14], 0, 32);
  544.         SetPt(playerSpeed[15], -14, 28);
  545.         SetPt(playerSpeed[0], -22, 22);
  546.         SetPt(playerSpeed[1], -28, 14);
  547.         SetPt(playerSpeed[2], -32, 0);
  548.         SetPt(playerSpeed[3], -28, -14);
  549.         SetPt(playerSpeed[4], -22, -22);
  550.         SetPt(playerSpeed[5], -14, -28);
  551.  
  552.         SetRect(r, 0, 0, 40, 40); {Total face size}
  553.         for i := 0 to 15 do
  554.             begin
  555.                 playerFace[i] := SATNewFace(r);
  556.                 SATChangedFace(playerFace[i]);
  557.             end;
  558.         RedrawPlayerFaces;
  559.     end;
  560.  
  561.     procedure HandlePlayer (me: SpritePtr);
  562.     begin
  563.         me^.mode := gMode;
  564.         me^.face := playerFace[me^.mode];
  565.  
  566.         me^.fixedPos.h := me^.fixedPos.h + playerSpeed[me^.mode].h;
  567.         me^.fixedPos.v := me^.fixedPos.v + playerSpeed[me^.mode].v;
  568.  
  569.         me^.position.h := BSR(me^.fixedPos.h, 4); {Shift left 4 steps, i.e. div 16}
  570.         me^.position.v := BSR(me^.fixedPos.v, 4);
  571.  
  572.         if me^.fixedPos.h < 0 then
  573.             begin
  574.                 me^.position.h := 0;
  575.                 me^.fixedPos.h := 0;
  576. {gMode := BitAnd(BitAnd(4 - gMode, 15) + 4, 15);}
  577.             end;
  578.         if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  579.             begin
  580.                 me^.position.h := gSAT.offSizeH - me^.hotRect.right;
  581.                 me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  582. {gMode := BitAnd(BitAnd(4 - gMode, 15) + 4, 15);}
  583.             end;
  584.         if me^.fixedPos.v < 0 then
  585.             begin
  586.                 me^.position.v := 0;
  587.                 me^.fixedPos.v := 0;
  588. {gMode := BitAnd(-gMode, 15);}
  589.             end;
  590.         if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  591.             begin
  592.                 me^.position.v := gSAT.offSizeV - me^.hotRect.bottom;
  593.                 me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  594. {gMode := BitAnd(-gMode, 15);}
  595.             end;
  596.     end;
  597.  
  598. {Get a vector from center to center of two sprites}
  599.     function Vector (s1, s2: SpritePtr): Point;
  600.     begin
  601.         Vector.h := s1^.position.h + s1^.face^.iconMask.bounds.right div 2 - s2^.position.h - s2^.face^.iconMask.bounds.right div 2;
  602.         Vector.v := s1^.position.v + s1^.face^.iconMask.bounds.right div 2 - s2^.position.v - s2^.face^.iconMask.bounds.right div 2;
  603.     end;
  604.  
  605. {Squared distance between centers of two sprites}
  606.     function Dist2 (s1, s2: SpritePtr): Longint;
  607.         var
  608.             v: Point;
  609.     begin
  610.         v := Vector(s1, s2);
  611.         Dist2 := v.h * v.h + v.v * v.v;
  612.     end;
  613.  
  614.     function RegionHitTest (s1, s2: SpritePtr): Boolean;
  615.         var
  616.             r1, r2: RgnHandle;
  617.     begin
  618. {We know that out hotRects coincide. However, that doesn't mean that we must take it as a}
  619. {collision! Rather, we can do more processing here to decide whether or not it was a collision.}
  620. {In this case, we copy the mask regions of each sprite, offset them to the proper positions,}
  621. {and check if they, too, overlap!}
  622. {}
  623. {Do you think we are doing double work, both dealing with hotRects and the regions? If you do,}
  624. {let me explain some more. The idea is that SAT checks the hotRects for you, which takes away}
  625. {next to all false hits. Checking hotRects is *fast*, so that's what we can afford to do all-to-all}
  626. {(or all-to-near, depending on the chosen search mode). Once a *possible* collision is detected,}
  627. {we can spend some time analyzing it further!}
  628.  
  629. {First of all, let's do some error checking. We could also have done this when loading the faces.}
  630. {Most programs won't have to bother whether or not the regions have been generated}
  631. {successfully, but when using them this way, they must exist or we may get a crash.}
  632.         if (s1^.face^.maskRgn = nil) or (s2^.face^.maskRgn = nil) then
  633.             begin
  634.                 ReportStr('Error: No mask region!');
  635.                 exit(RegionHitTest);
  636.             end;
  637.  
  638. {Make copies of the mask regions and offset them to the proper places.}
  639.         r1 := NewRgn;
  640.         r2 := NewRgn;
  641.         CopyRgn(s1^.face^.maskRgn, r1);
  642.         CopyRgn(s2^.face^.maskRgn, r2);
  643.         OffsetRgn(r1, s1^.position.h, s1^.position.v);
  644.         OffsetRgn(r2, s2^.position.h, s2^.position.v);
  645.  
  646.         SectRgn(r1, r2, r1);                    {Is there any overlap?}
  647.  
  648. {If empty, no collision, otherwise, handle the collision!}
  649.         RegionHitTest := not EmptyRgn(r1);
  650.  
  651.         DisposeRgn(r1);
  652.         DisposeRgn(r2);
  653.     end;
  654.  
  655.     procedure HitPlayer (me, him: SpritePtr);
  656.         var
  657.             v: Point;
  658.     begin
  659.         if RegionHitTest(me, him) then {Do the sprites *really* overlap?}
  660.             begin
  661.  
  662.                 if Dist2(me, him) > 60 then
  663.                     begin
  664. {Hit too far out, so let's call it the outside. Bounce away him.}
  665. {We could make more efforts here for a good bounce.}
  666.                         him^.position.h := him^.position.h + me^.speed.h;
  667.                         him^.speed.h := -him^.speed.h + me^.speed.h;
  668.                         him^.position.v := him^.position.v + me^.speed.v;
  669.                         him^.speed.v := -him^.speed.v + me^.speed.v;
  670. {Finally, make sure the other is moving *away* from us!}
  671. {And when we're at it, why not move it just a little, too?}
  672. {Yuck, this is ugly! Yup, careless programming. Hack, hack!}
  673.                         v := Vector(me, him);
  674.                         if v.h > 0 then
  675.                             begin
  676.                                 if him^.speed.h > 0 then
  677.                                     him^.speed.h := -him^.speed.h;
  678.                                 him^.position.h := him^.position.h - 1;
  679.                             end
  680.                         else
  681.                             begin
  682.                                 if v.h < 0 then
  683.                                     if him^.speed.h < 0 then
  684.                                         him^.speed.h := -him^.speed.h;
  685.                                 him^.position.h := him^.position.h + 1;
  686.                             end;
  687.                         if v.v > 0 then
  688.                             begin
  689.                                 if him^.speed.v > 0 then
  690.                                     him^.speed.v := -him^.speed.v;
  691.                                 him^.position.v := him^.position.v - 1;
  692.                             end
  693.                         else
  694.                             begin
  695.                                 if v.v < 0 then
  696.                                     if him^.speed.v < 0 then
  697.                                         him^.speed.v := -him^.speed.v;
  698.                                 him^.position.v := him^.position.v + 1;
  699.                             end;
  700.  
  701.                     end
  702.                 else
  703.                     begin
  704. {This looks like inside! Let's eat him.}
  705.                         score := score + 1;
  706.                         RedrawScoreFace;
  707.                         him^.task := nil;
  708.                         SATSoundPlay(bippH, 1, true);
  709.                     end; {Dist2}
  710.             end; {RegionHitTest}
  711.  
  712.     end;
  713.  
  714.     procedure InitScoreFace;
  715.         var
  716.             r: Rect;
  717.     begin
  718.         SetRect(r, 0, 0, 80, 14);{}
  719.         scoreFace := SATNewFace(r);
  720.         SATChangedFace(scoreFace);
  721.         SetRect(r, 0, 0, 200, 16);{}
  722.         highFace := SATNewFace(r);
  723.         SATChangedFace(highFace);
  724.         SetRect(r, 0, 0, 120, 16);{}
  725.         lastFace := SATNewFace(r);
  726.         SATChangedFace(lastFace);
  727.     end;
  728.  
  729.     procedure SetupDummy (me: SpritePtr);
  730.     begin
  731.         me^.task := @SATBounce;
  732.     end;
  733.  
  734.     procedure SetupSmall (me: SpritePtr);
  735.     begin
  736.         me^.face := myFace;
  737.         me^.hotRect := me^.face^.iconMask.bounds;
  738.         me^.task := @HandleTheSprite;
  739.  
  740.         me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  741.         me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  742.     end;
  743.  
  744.     procedure SetupPlayer (me: SpritePtr);
  745.     begin
  746.         me^.face := playerFace[0];
  747.         me^.hotRect := me^.face^.iconMask.bounds;
  748.         me^.task := @HandlePlayer;
  749.         me^.hitTask := @HitPlayer;
  750.  
  751.         me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  752.         me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  753.     end;
  754.  
  755.  
  756.     procedure NewSet;
  757.         var
  758.             sp: SpritePtr;
  759.             i: integer;
  760.     begin
  761. {Kill all sprites}
  762.         while gSAT.sRoot <> nil do
  763.             SATKillSprite(gSAT.sRoot);
  764.  
  765. {Create the pills}
  766.         for i := 1 to 10 do
  767.             sp := SATNewSprite(-1, Rand(gSAT.offSizeH - 32), Rand(gSAT.offSizeV - 32), @SetupSmall);
  768.         if settings^^.high > 7 then
  769.             for i := 8 to settings^^.high do
  770.                 sp := SATNewSprite(-1, Rand(gSAT.offSizeH - 32), Rand(gSAT.offSizeV - 32), @SetupSmall);
  771.  
  772.         sp := SATNewSprite(0, Rand(gSAT.offSizeH - 32), Rand(gSAT.offSizeV - 32), @SetupDummy);
  773.         RedrawScoreFace;
  774.         sp^.face := scoreFace;
  775.         repeat
  776.             sp^.speed.h := Rand(5) - 2
  777.         until sp^.speed.h <> 0;
  778.         repeat
  779.             sp^.speed.v := Rand(3) - 1
  780.         until sp^.speed.v <> 0;
  781.         sp^.hotRect := sp^.face^.iconMask.bounds;
  782. {Hoppsan- fattas nåt!}
  783.  
  784.         sp := SATNewSprite(1, (gSAT.offSizeH - 32) div 2, (gSAT.offSizeV - 32) div 2, @SetupPlayer);
  785.         gMode := 0;
  786.  
  787.         SATBackChanged(gSAT.bounds);
  788.         setCount := setCount + 1;
  789.         FlushEvents(6, 0); {Glöm klick från förra uppsättningen!}
  790.         if not (TickCount > gameStartTime + kGameTime) then {Om tiden INTE är ute så skall vi ändra!}
  791.             lastSetStartTime := TickCount;
  792.     end;
  793.  
  794. {An example of how you can (with some effort) scale a sprite.}
  795.     procedure ScaleWelcomeFace;
  796.         var
  797.             srcFacePort, destFacePort: GrafPtr;
  798.             i: integer;
  799.             scaleRect: Rect;
  800.     begin
  801. {Get the rectangle of the original}
  802.         scaleRect := welcomeFace^.iconMask.bounds;
  803.  
  804.         for i := 0 to 31 do
  805.             begin
  806. {SetPortFace to the source. This must be done each turn since ChangedFace changes it.}
  807.                 SATSetPortFace(welcomeFace); {Set the FIRST of SAT's two internal face-ports to the original face.}
  808.                 GetPort(srcFacePort); {Get the port.}
  809. {Modify the size}
  810.                 scaleRect.bottom := scaleRect.bottom - 2;
  811.                 scaleRect.right := scaleRect.right - 2;
  812. {Create the new face}
  813.                 if scaledFace[i] = nil then
  814.                     scaledFace[i] := SATNewFace(scaleRect);
  815. {Get a port to it}
  816.                 SATSetPortFace2(scaledFace[i]); {Set the SECOND of SAT's two internal face ports to the new face.}
  817.                 GetPort(destFacePort); {Get the port.}
  818. {Copy the image}
  819.                 CopyBits(srcFacePort^.portBits, destFacePort^.portBits, welcomeFace^.iconMask.bounds, scaleRect, srcCopy + ditherCopy, nil);
  820.                 CopyBits(welcomeFace^.iconMask, scaledFace[i]^.iconMask, welcomeFace^.iconMask.bounds, scaleRect, srcCopy, nil);
  821.                 SATChangedFace(scaledFace[i]); {Done changing it. Tell SAT that it may do whatever it needs.}
  822.             end; {for}
  823.     end; {ScaleWelcomeFace}
  824.  
  825.     procedure WindUpdate (whatever: Boolean);
  826.         var
  827.             savePort: GrafPtr;
  828.             saveDev: GDHandle;
  829.     begin
  830.         if SATDepthChangeTest then
  831. {IMPORTANT! We must redraw all internally generated faces on depth changes!}
  832.             begin
  833.                 ReDrawPlayerFaces;
  834.                 RedrawScoreFace;
  835.                 RedrawHighFace;
  836.                 RedrawLastFace;
  837.                 ScaleWelcomeFace;
  838.  
  839. {We also have to redraw the background, since it's not a PICT (in which case that is automatic)}
  840.                 GetPort(savePort);
  841.                 if gSAT.colorFlag then
  842.                     saveDev := GetGDevice;
  843.                 SATSetPortBackScreen;
  844.                 SATPenPat(bgPat);
  845.                 PaintRect(gSAT.backScreen^.portRect);
  846.                 PenNormal;
  847.                 CopyBits(gSAT.backScreen^.portBits, gSAT.offScreen^.portBits, gSAT.offScreen^.portRect, gSAT.offScreen^.portRect, srcCopy, nil);
  848.                 SetPort(savePort);
  849.                 if gSAT.colorFlag then
  850.                     SetGDevice(saveDev);
  851.  
  852.             end;
  853.         SATRedraw;
  854.     end;
  855.  
  856.     procedure WindClose;
  857.     begin
  858.         SkelWhoa;
  859.     end;
  860.  
  861.     procedure WindMouse (where: Point; when: Longint; modifiers: integer);
  862.         var
  863.             found, sp: SpritePtr;
  864.             anyLeft: Boolean;
  865.             myRegion: RgnHandle;
  866.     begin
  867. {Not needed for the game, but note that we can check the mask region of a sprite}
  868. {towards a mouse click as well as a colliding sprite! For demonstrating this, mouse}
  869. {clicks are processed, and if a sprite is hit, a SysBeep is made. Try this by clicking}
  870. {in and around the "Hello" sprite!}
  871.  
  872.         myRegion := NewRgn;
  873.         sp := gSAT.sRoot;
  874.         found := nil;
  875.         while sp <> nil do                                        {Search through the sprite list}
  876.             begin
  877.                 if PtInRect(where, sp^.r) then                        {We are in the rect!}
  878.                     if sp^.face <> nil then                            {Does it have a face at all? Remember it's legal not to have one!}
  879.                         if sp^.face^.maskRgn <> nil then                {Does that face have a mask region? It should, but…}
  880.                             begin
  881.                                 CopyRgn(sp^.face^.maskRgn, myRegion);    {Copy the mask region}
  882.                                 OffsetRgn(myRegion, sp^.position.h, sp^.position.v);        {Offset it to the position of the sprite}
  883.                                 if PtInRgn(where, myRegion) then            {Are we in the region?}
  884.                                     found := sp;                                    {Yes!}
  885.                             end;
  886.                 sp := sp^.next;                                        {Next sprite…}
  887.             end;
  888.         if found <> nil then
  889.             SysBeep(1);                                            {We hit something. Tell us so.}
  890.         DisposeRgn(myRegion);
  891.     end;
  892.  
  893.  
  894.     procedure WindKey (theKey: char; theMods: integer);
  895.     begin
  896. {Hard-coded keys; real games have customizable keys.}
  897.         case theKey of
  898.             ',', 'z', '1': 
  899.                 gMode := BitAnd(gMode - 1, 15);
  900.             '.', 'x', '2': 
  901.                 gMode := BitAnd(gMode + 1, 15);
  902.             otherwise
  903.         end; {case}
  904.         ObscureCursor; {Hide the cursor until the mouse is moved.}
  905.     end;
  906.  
  907.     procedure SetupSAT (theWind: WindowPtr);{Calls SATCustomInit and paints the background with a pattern}
  908.         var
  909.             saveport: GrafPtr;
  910.             savedev: GDHandle;
  911.     begin
  912.         SATCustomInit(0, 0, theWind^.portRect, theWind, nil, false, false, false, true, false); {Nytt försök!}
  913.  
  914. {We use a customized sprite record! Thus, we must SetSpriteSize before creating sprites!}
  915.         SATSetSpriteRecSize(sizeof(Sprite));
  916.  
  917.         if bgPat = nil then
  918.             bgPat := SATGetPat(128);
  919.         if bgPat = nil then
  920.             Barf;
  921.  
  922.         SATGetPort(savePort, saveDev);
  923.  
  924.         SATSetPortBackScreen;
  925.         SATPenPat(bgPat);
  926.         PaintRect(gSAT.backScreen^.portRect);
  927.         PenNormal;
  928.         CopyBits(gSAT.backScreen^.portBits, gSAT.offScreen^.portBits, gSAT.offScreen^.portRect, gSAT.offScreen^.portRect, srcCopy, nil);
  929.  
  930.         SATSetPort(savePort, saveDev);
  931.         CopyBits(gSAT.backScreen^.portBits, gSAT.wind^.portBits, gSAT.wind^.portRect, gSAT.wind^.portRect, srcCopy, nil);
  932.  
  933.         if SkelWindow(theWind, @WindMouse, @WindKey, @WindUpdate, nil, @WindClose, nil, nil, false) then
  934.             ;
  935.     end;
  936.  
  937.     procedure SetupWindow;
  938.         var
  939.             slaskWind, theWind: WindowPtr;
  940.             tmpWorld: SysEnvRec;
  941.             tmpCol: Boolean;
  942. {r: Rect;}
  943. {peek: WindowPeek;}
  944.     begin
  945.         tmpCol := false;
  946.         if noErr = SysEnvirons(1, tmpWorld) then
  947.             tmpCol := tmpWorld.hasColorQD;
  948.  
  949.         if tmpCol then
  950.             theWind := GetNewCWindow(theWindRes, nil, WindowPtr(-1))
  951.         else
  952.             theWind := GetNewWindow(theWindRes, nil, WindowPtr(-1));
  953.  
  954. {peek := WindowPeek(theWind);}
  955.  
  956.         if theWind = nil then
  957.             Barf;
  958.  
  959. {MoveWindow(theWind, 50, 50, false);}
  960.  
  961. {r := WindowPeek(theWind)^.contRgn^^.rgnBBox;}
  962.  
  963. {slaskWind := theWind;}
  964.  
  965.         SetupSAT(theWind); {Calls SATCustomInit and paints the background with a pattern}
  966.  
  967.         ShowWindow(gSAT.wind);
  968.         SelectWindow(gSAT.wind);
  969.         SetPort(gSAT.wind);
  970.         SATRedraw;
  971.     end;
  972.  
  973. {The task the welcome sprite has while zooming.}
  974.     procedure ZoomWelcome (me: SpritePtr);
  975.     begin
  976.         me^.mode := me^.mode + 1;
  977. {Compensate for the size change to make it centered in one place.}
  978.         me^.position.h := me^.position.h - 1;
  979.         me^.position.v := me^.position.v - 1;
  980.         if me^.mode >= 32 then
  981.             begin
  982.                 me^.face := welcomeFace;
  983.                 me^.task := @SATBounce;
  984.             end
  985.         else
  986.             me^.face := scaledFace[32 - me^.mode];
  987.     end;
  988.  
  989. {Initialize faces.}
  990.     procedure InitSpriteFaces;
  991.         var
  992.             i: integer;
  993.     begin
  994.         myFace := SATGetFace(128);
  995.         if myFace = nil then
  996.             Barf;
  997.         welcomeFace := SATGetFace(138);
  998.         if welcomeFace = nil then
  999.             Barf;
  1000. {We don't HAVE to bail out when a face fails to load - the program will stll wor, but that face will}
  1001. {not be visible.}
  1002.         ScaleWelcomeFace;
  1003.     end;
  1004.  
  1005.     var
  1006.         lastTime: Longint;
  1007.  
  1008. {DirtyWork is called from TransSkel}
  1009.     procedure DirtyWork;
  1010.         var
  1011.             sp: SpritePtr;
  1012.             ph: PicHandle;
  1013.             r: Rect;
  1014.             savePort: GrafPtr;
  1015.             saveDev: GDHandle;
  1016.     begin
  1017. {We can check TickCount as usual, since we never know how often we get null events.}
  1018.         if lastTime + 1 < TickCount then
  1019.             begin
  1020.                 SATRun(false);
  1021.                 lastTime := tickCount;
  1022.             end;
  1023.  
  1024.         if gameRunning then
  1025.             begin
  1026. {Timebar}
  1027.                 GetPort(savePort);
  1028.                 if gSAT.colorFlag then
  1029.                     saveDev := GetGDevice;
  1030.                 SATSetPortBackScreen;
  1031. {I *should* change only the part that actually changes!}
  1032.                 r := gSAT.wind^.portRect;
  1033.                 SATBackChanged(r);
  1034.                 r.right := 5;
  1035.                 r.top := r.bottom * (lastSetStartTime + kGameTime - TickCount) div kGameTime;
  1036.                 ForeColor(redColor); {Quickest way to get a color.}
  1037.                 PaintRect(r);
  1038.                 r.bottom := r.top;
  1039.                 r.top := 0;
  1040.                 SATPenPat(bgPat);
  1041.                 PaintRect(r);
  1042.                 PenNormal;
  1043.  
  1044.                 SetPort(savePort);
  1045.                 if gSAT.colorFlag then
  1046.                     SetGDevice(saveDev);
  1047. {end of Timebar}
  1048.  
  1049.                 if TickCount > lastSetStartTime + kGameTime then
  1050.                     begin
  1051.                         SATSoundPlay(baeH, 5, true);
  1052. {NewSet;}
  1053.  
  1054.                         if TickCount > gameStartTime + kGameTime then
  1055.                             begin
  1056.                                 if score > settings^^.high then
  1057.                                     begin
  1058. {settings^^.high := score;}
  1059.                                         SATSoundEvents;
  1060.                                         AskHigh;
  1061. {ReportStr(stringof('New highscore: ', score, ' points! Whoopie!'));}
  1062.                                         ChangedResource(Handle(settings));
  1063.                                     end;
  1064.  
  1065. {Kill all sprites}
  1066.                                 while gSAT.sRoot <> nil do
  1067.                                     SATKillSprite(gSAT.sRoot);
  1068.  
  1069.                                 RedrawHighFace;
  1070.                                 RedrawLastFace;
  1071. {Time for breaking some of my conventions! The stuff below should be done in "setup" and "handle"}
  1072. {routines, as recommened in the manual and done in other demos - but if we want to mess up the code,}
  1073. {we are free to do so! The sprites below set up their faces and speeds right here, and share a common}
  1074. {handling routine (SATBounce).}
  1075.  
  1076. {Make the "hello" sprite}
  1077.                                 sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 2, @SetupDummy);
  1078.                                 sp^.face := welcomeFace;
  1079.                                 repeat
  1080.                                     sp^.speed.h := Rand(3) - 1
  1081.                                 until sp^.speed.h <> 0;
  1082.                                 repeat
  1083.                                     sp^.speed.v := Rand(3) - 1
  1084.                                 until sp^.speed.v <> 0;
  1085.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1086.                                 sp^.task := @ZoomWelcome;
  1087. {High score sprite}
  1088.                                 sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 4, @SetupDummy);
  1089.                                 sp^.face := highFace;
  1090.                                 repeat
  1091.                                     sp^.speed.h := Rand(7) - 3
  1092.                                 until sp^.speed.h <> 0;
  1093.                                 repeat
  1094.                                     sp^.speed.v := Rand(3) - 1
  1095.                                 until sp^.speed.v <> 0;
  1096.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1097. {Last score sprite}
  1098.                                 sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 3, @SetupDummy);
  1099.                                 sp^.face := lastFace;
  1100.                                 repeat
  1101.                                     sp^.speed.h := Rand(7) - 3
  1102.                                 until sp^.speed.h <> 0;
  1103.                                 repeat
  1104.                                     sp^.speed.v := Rand(3) - 1
  1105.                                 until sp^.speed.v <> 0;
  1106.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1107.  
  1108.                                 SATSetPortScreen;
  1109.                                 SATRedraw; {Just to make sure killed sprites are erased}
  1110.                                 gameRunning := false;
  1111.                             end;
  1112.  
  1113.                     end;
  1114.             end;
  1115.  
  1116.         if not gameRunning then
  1117.             if gSAT.sRoot = nil then
  1118.                 begin
  1119. {Messy code for setting up the "hello" sprite - which is why I recommend the use of setup routines.}
  1120.                     sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 2, @SetupDummy);
  1121.                     sp^.face := welcomeFace;
  1122.                     repeat
  1123.                         sp^.speed.h := Rand(3) - 1
  1124.                     until sp^.speed.h <> 0;
  1125.                     repeat
  1126.                         sp^.speed.v := Rand(3) - 1
  1127.                     until sp^.speed.v <> 0;
  1128.                     sp^.hotRect := sp^.face^.iconMask.bounds;
  1129.                     sp^.task := @ZoomWelcome;
  1130.                 end;
  1131.     end;
  1132.  
  1133.     procedure InitHigh;
  1134.     begin
  1135.         settings := SettingsHnd(GetResource('Sett', 0));
  1136.         if settings = nil then {Didn't exist - create it!}
  1137.             begin
  1138.                 settings := SettingsHnd(NewHandle(Sizeof(SettingsRec)));
  1139.                 if settings = nil then
  1140.                     begin
  1141.                         SysBeep(1);
  1142.                         halt;
  1143.                     end;
  1144.                 settings^^.high := 0;
  1145.                 AddResource(handle(settings), 'Sett', 0, '');
  1146.             end
  1147.         else {Did exist - check the size!}
  1148.             if GetHandleSize(Handle(settings)) < sizeof(SettingsRec) then
  1149.                 SetHandleSize(Handle(settings), sizeof(SettingsRec));
  1150.     end;
  1151.  
  1152.     procedure DoFileMenu (item: integer);
  1153.     begin
  1154.         case item of
  1155.             newGameItem: 
  1156.                 begin
  1157.                     score := 0;
  1158.                     gameRunning := true;
  1159.                     gameStartTime := TickCount;
  1160.                     lastSetStartTime := TickCount;
  1161.                     setCount := 0;
  1162.                     NewSet;
  1163.  
  1164.                     ObscureCursor; {Hide the cursor until the mouse is moved.}
  1165.                 end;
  1166.             clearHighItem: 
  1167.                 if QuestionStr('Set the high score to zero?') then
  1168.                     begin
  1169.                         settings^^.high := 0;
  1170.                         ChangedResource(handle(settings));
  1171.                     end;
  1172.             otherwise
  1173.                 SkelWhoa;
  1174.         end;
  1175.     end;
  1176.  
  1177.     procedure DoShapeMenu (item: integer);
  1178.         const
  1179.             wide = 1;
  1180.             tall = 2;
  1181.         var
  1182.             p: Point;
  1183.     begin
  1184.         p := gSAT.wind^.portRect.botRight;
  1185.         case item of
  1186.             wide: 
  1187.                 if gSAT.wind^.portRect.bottom > gSAT.wind^.portRect.right then
  1188.                     begin
  1189.                         CheckItem(shapeMenu, wide, true);
  1190.                         CheckItem(shapeMenu, tall, false);
  1191.                         SizeWindow(gSAT.wind, p.v, p.h, false); {swap size}
  1192.                         SATKill;
  1193.                         SetupSAT(gSAT.wind);
  1194.                         gameRunning := false;
  1195.                     end;
  1196.             tall: 
  1197.                 if gSAT.wind^.portRect.bottom < gSAT.wind^.portRect.right then
  1198.                     begin
  1199.                         CheckItem(shapeMenu, tall, true);
  1200.                         CheckItem(shapeMenu, wide, false);
  1201.                         SizeWindow(gSAT.wind, p.v, p.h, false); {swap size}
  1202.                         SATKill;
  1203.                         SetupSAT(gSAT.wind);
  1204.                         gameRunning := false;
  1205.                     end;
  1206.             otherwise
  1207.                 SysBeep(1);
  1208.         end;{case}
  1209.     end;
  1210.  
  1211.     procedure SetUpMenus;
  1212.     begin
  1213.         SkelApple('About CollisionIII…', @DoAbout);
  1214.         fileMenu := GetMenu(fileMenuRes);
  1215.         if fileMenu = nil then
  1216.             Barf;
  1217.         if SkelMenu(fileMenu, @DoFileMenu, nil, true) then
  1218.             ;
  1219.         shapeMenu := GetMenu(shapeMenuRes);
  1220.         if shapeMenu = nil then
  1221.             Barf;
  1222.         if SkelHMenu(shapeMenu, @DoShapeMenu, nil) then {Install as hierarcical menu}
  1223.             ;
  1224.         CheckItem(shapeMenu, 1, true); {Check "wide"}
  1225.     end;
  1226.  
  1227. begin
  1228.     SkelInit(6, nil);
  1229.     SkelSetSleep(0); {Tell TransSkel that we want attention as often as possible.}
  1230.     SetupMenus;
  1231.     SetupWindow;
  1232.     InitHigh;
  1233.     InitSpriteFaces;
  1234.     InitScoreFace;
  1235.     InitPlayerFaces;
  1236.     SkelBackground(@DirtyWork);
  1237.     lastTime := TickCount;
  1238. {$IFC UNDEFINED THINK_PASCAL}
  1239.     qd.randSeed := TickCount;
  1240. {$ELSEC}
  1241.     randSeed := TickCount;
  1242. {$ENDC}
  1243.  
  1244.     Synth; {Build sounds!}
  1245.     SATSoundShutup;
  1246.  
  1247.     SkelMain;
  1248.     SkelClobber;
  1249.     SATSoundShutup;
  1250. end.